%{
#include<stdio.h>
#include<stdlib.h>
#include"symbol.h"
#include"absyn.h"
#include"env.h"
#include"interpret.h"
int block = 0;


#define YYERROR_VERBOSE 1

extern struct table* tytable;
extern struct table* valtable;
%}

%union{
	struct symbol* sym;
	struct ast* a;
	struct CM_vardeclist* vdl;
	struct CM_vardecl* vd;
	struct CM_typedecl* td;
	struct CM_decl* d;
	struct CM_exp* e;
	struct CM_stmtlist* stl;
	struct CM_stmt* s;
	struct CM_type* ty;
	struct CM_assign* as;
	struct CM_for* f;
	struct CM_while* w;
	struct CM_if* i;
	struct CM_var* v;
	struct CM_varlist* vl;
	struct CM_fundecl* fd;
	int cmp;
}

%token <sym> INTEGER FLOAT CHAREACTER IDENTIFIER 
%token <sym> INT VOID DOUBLE CHAR
%token EOL STRUCT FOR WHILE IF RETURN
%nonassoc LOW_THAN_ELSE
%nonassoc ELSE

%nonassoc <cmp> CMP

%right '='
%left '+' '-'
%left '*' '/'
%nonassoc '|' UMINUS

%type <stl> stmtlist
%type <s> stmt
%type <i> if_stmt
%type <w> while_stmt
%type <f> for_stmt
%type <v> var
%type <as> assign_stmt
%type <e> exp
%type <d> decl
%type <vd> vardecl
%type <vl> varlist vars
%type <vdl> vardeclist paramlist params
%type <td> typedecl
%type <fd> fundecl
%type <ty> type
%type <sym> simple_type struct_type

%start program

%%

program:		/* empty */
				| program stmt EOL { showStmtValue(tytable, valtable, interstmt(tytable, valtable, $2)); printf("> "); }
;
stmtlist: /* empty */ { $$ = NULL; }
			|  stmt stmtlist { $$ = newstmtlist($1, $2); }
;

stmt: exp ';' { $$ = newstmt(cm_exp, $1); } 
		| '{' stmtlist '}' { $$ = newstmt(cm_blockstmt, $2); } 
		| decl  { $$ = newstmt(cm_decl, $1); } 
		| assign_stmt ';' { $$ = newstmt(cm_assign, $1); }
		| if_stmt { $$ = newstmt(cm_if, $1); }
		| while_stmt { $$ = newstmt(cm_while, $1); }
		| for_stmt { $$ = newstmt(cm_for, $1); }
		| RETURN exp ';' { $$ = newstmt(cm_return, $2); }
		| RETURN ';'	{ $$ = newstmt(cm_return, NULL); }
;

if_stmt:   IF '(' exp ')' stmt ELSE stmt { $$ = newif($3, $5, $7); }
		 | IF '(' exp ')' stmt	%prec LOW_THAN_ELSE { $$ = newif($3, $5, NULL); }
;
while_stmt:  WHILE '(' exp ')' stmt { $$ = newwhile($3, $5); }
;

for_stmt:	 FOR '(' assign_stmt ';' exp ';' assign_stmt ')' stmt { $$ = newfor($3, $5, $7, $9); }
;

exp:  exp '+' exp { $$ = newexp(cm_plus, $1, $3); }
	| exp '-' exp { $$ = newexp(cm_minus, $1, $3); }
	| exp '*' exp { $$ = newexp(cm_mul, $1, $3); }
	| exp '/' exp { $$ = newexp(cm_div, $1, $3); }
	| exp CMP exp { $$ = newexp(getCmpOp($2), $1, $3); }
	| '(' exp ')' { $$ = newexp(cm_parent, $2, NULL); }
	| var  { $$ = newexp(cm_var, $1, NULL); }
	| CHAREACTER  { $$ = newexp(cm_const_char, $1, NULL); }
	| FLOAT       { $$ = newexp(cm_const_float, $1, NULL); }
	| INTEGER     { $$ = newexp(cm_const_int, $1, NULL); }
	| IDENTIFIER '(' vars ')' { $$ = newexp(cm_funcall, $1, $3); }
;

assign_stmt: var '=' exp  { $$ = newassign($1, $3); }
;
vars: /* empty */ { $$ = NULL; }
	| exp varlist { $$ = newvarlist($1, $2); }

varlist: /* empty */ { $$ = NULL; }
		| ',' exp varlist { $$ = newvarlist($2, $3); }
;
var: IDENTIFIER { $$ = newvar(var_abs_id, $1, NULL); }
   | IDENTIFIER '[' exp ']' { $$ = newvar(var_abs_array, $1, $3); }
   | IDENTIFIER '.' IDENTIFIER { $$ = newvar(var_abs_struct, $1, $3); }
;

decl:	vardecl ';' { $$ = newdecl(cm_vardecl, $1); }
	| typedecl { $$ = newdecl(cm_typedecl, $1); }
	| fundecl { $$ = newdecl(cm_fundecl, $1); }
;

typedecl: STRUCT IDENTIFIER '{' vardeclist '}' { $$ = newtypedecl($2, $4); }
;

fundecl: type IDENTIFIER '(' params ')' '{' stmtlist '}' { $$ = newfundecl($1, $2, $4, $7); }
;

params: /* empty */ { $$ = NULL; }
	  | vardecl paramlist { $$ = newvardeclist($1, $2); }
;
paramlist: /* empty */ { $$ = NULL; }
		|  ',' vardecl paramlist { $$ = newvardeclist($2, $3); }
;
vardeclist:	/* empty  */ { $$ = NULL; }
					| vardecl ';' vardeclist { $$ = newvardeclist($1, $3); }
;
vardecl: type IDENTIFIER { $$ = newvardecl($1, $2); }
		|		 type IDENTIFIER '[' exp ']' { $$ = newarrdecl($1, $2, $4); }
;

type: simple_type { $$ = newtype(cm_simple, $1);}
		| struct_type { $$ = newtype(cm_struct, $1);}
;

simple_type: INT {$$ = $1;}
						| DOUBLE {$$ = $1;}
						| CHAR {$$=$1;}
						| VOID {$$=$1;}
;

struct_type: IDENTIFIER	{ $$ = $1; }
;

%%
